/*
============================================================================
 Name        : AudioscrobblerEngine.cpp
 Author      : 
 Version     :
 Copyright   : Your copyright notice
 Description : CAudioscrobblerEngine implementation
============================================================================
*/

#include "AudioscrobblerEngine.h"
#include "PluginUtility.h"

#include "Md5.h"
#include "XmlEngine.h"
#include "Playlist.h"

_LIT8(KLastFmSessionTag, "session=");
_LIT8(KLastFmErrorMessage, "msg=");

_LIT(KLastFmXspfPlaylist, "playlist");
_LIT(KLastFmXspfPlaylistTitle, "playlist/title");
_LIT(KLastFmXspfTrack, "playlist/trackList/track");
_LIT(KLastFmXspfTrackLocation, "playlist/trackList/track/location");
_LIT(KLastFmXspfTrackTitle, "playlist/trackList/track/title");
_LIT(KLastFmXspfTrackArtist, "playlist/trackList/track/creator");
_LIT(KLastFmXspfTrackAlbum, "playlist/trackList/track/album");
_LIT(KLastFmXspfTrackImageUrl, "playlist/trackList/track/image");


CAudioscrobblerEngine::CAudioscrobblerEngine(MAudioscrobblerObserver& aObserver)
: iObserver(aObserver), iPlaylist(NULL), iRequestType(EScrobblerUndefined)
{
}

CAudioscrobblerEngine::~CAudioscrobblerEngine()
{
	DELETE(iPlaylist);
}

CAudioscrobblerEngine* CAudioscrobblerEngine::NewLC(MAudioscrobblerObserver& aObserver)
{
	CAudioscrobblerEngine* self = new (ELeave)CAudioscrobblerEngine(aObserver);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
}

CAudioscrobblerEngine* CAudioscrobblerEngine::NewL(MAudioscrobblerObserver& aObserver)
{
	CAudioscrobblerEngine* self=CAudioscrobblerEngine::NewLC(aObserver);
	CleanupStack::Pop(); // self;
	return self;
}

void CAudioscrobblerEngine::ConstructL()
{
	//Create a reference to connection object
	iConnection = CConnection::Static();
}

void CAudioscrobblerEngine::ParseXmlL(TDesC8& aResponse)
{
	//Parse xml response
	CXmlEngine* iXmlEngine = CXmlEngine::NewL(*this);
	TPtrC8 ptrC8((TUint8*)aResponse.Ptr(), aResponse.Size());
	iXmlEngine->ParseL(ptrC8, ETrue);
	DELETE(iXmlEngine);
}

void CAudioscrobblerEngine::RequestPlaylistL(const TDesC& aUsername, const TDesC& aPassword)
{
	iRequestType = EScrobblerHandshake;

	//Reset data
	iSession.Zero();
	iErrorDesc.Zero();

	//Create url	
	TBuf<1000> url;
	url.Copy(_L("http://ws.audioscrobbler.com"));

	//Handshake url
	url.Append(_L("/radio/handshake.php?version=1.0.0.1&platform=linux&username="));
	
	//Username
	url.Append(aUsername);
	
	//Password (we need calculat md5)
	url.Append(_L("&passwordmd5="));

	//Calculate md5(password)
	CMd5* md5 = CMd5::NewL();
	url.Append(md5->ComputeMD5(aPassword));
	delete md5;

	//Language
	url.Append(_L("&language=en"));

	//Make request
	iConnection->SetObserver(this);
	iConnection->RequestL(url);
}

void CAudioscrobblerEngine::XspfL()
{
	iRequestType = EScrobblerXspf;

	//Reset data
	DELETE(iPlaylist);
	iErrorDesc.Zero();

	//Create url	
	TBuf<1000> url;
	url.Copy(_L("http://ws.audioscrobbler.com"));
	url.Append(_L("/radio/xspf.php?sk="));
	url.Append(iSession);
	url.Append(_L("&discovery=0&desktop=1.0.0.1"));

	//Make request
	iConnection->SetObserver(this);
	iConnection->RequestL(url);
}

void CAudioscrobblerEngine::ParseHandshakeL(TDesC8& aResponse)
{
	TUint8* bufferToDecode = const_cast<TUint8*>(aResponse.Ptr());
	TInt length = aResponse.Size();

	if(length == 0)
	{
		//FinishedL();
		return;
	}

	//Initialize pointer to buffer
	TPtr8 bufferToDecodePtr(bufferToDecode, length, length);
	TPtr8 lineToDecodePtr(NULL, 0);
	TInt lastReadPos = 0;

	//Handshake: parse lines
	while (ETrue) // Parse the response header
	{
		//Search for \n
		TInt pos = bufferToDecodePtr.Locate(10);
		if(pos == KErrNotFound)
		{
			//Last line
			lineToDecodePtr.Set(bufferToDecode + lastReadPos, bufferToDecodePtr.Length(), bufferToDecodePtr.Length());
		}
		else
		{
			//Detected line to parse
			lineToDecodePtr.Set(bufferToDecode + lastReadPos, pos, pos);
		}

		//Parse line
		ParseLineL(lineToDecodePtr);

		//Remove line parsed from buffer
		lastReadPos += pos + 1;

		//Check if is the end of response headers
		if (pos == KErrNotFound)
		{
			break;
		}

		//Set next line to parse
		bufferToDecodePtr.Set(bufferToDecode + lastReadPos, bufferToDecodePtr.Length() - pos - 1, bufferToDecodePtr.Length() - pos - 1);
	}
}

void CAudioscrobblerEngine::ParseLineL(TDesC8& aLine)
{
	//Search for session
	if (GetValueL(aLine, KLastFmSessionTag, &iSession)) return;

	//Search for error message
	if (GetValueL(aLine, KLastFmErrorMessage, &iErrorDesc)) return;
}

TBool CAudioscrobblerEngine::GetValueL(TDesC8& aLine, const TDesC8& aKey, TDes* value)
{
	if (aLine.FindC(aKey) != 0) return EFalse;
	
	TInt pos = aLine.Locate('=');
	if(pos == KErrNotFound) return EFalse;

	value->Copy(aLine.Right(aLine.Length() - pos - 1));
	value->Trim();

	return ETrue;
}

void CAudioscrobblerEngine::StartElementRead(const TDesC& aNodeName, const RArray<TXmlAttribute> /*aAttributes*/)
{
	if (aNodeName.Compare(KLastFmXspfPlaylist) == 0)
	{
		//Create playlist
		iPlaylist = new TPlaylist();
	}
	else if (aNodeName.Compare(KLastFmXspfTrack) == 0)
	{
		//Create new track
		TTrack* track = new TTrack();
		iPlaylist->AppendTrackL(track);
		iCurrentTrack = track;
	}
}

void CAudioscrobblerEngine::EndElementRead(const TDesC& aNodeName, const TDesC& aData)
{
	if (aNodeName.Compare(KLastFmXspfPlaylistTitle) == 0)
	{
		//Set playlist title
		iPlaylist->SetTitle(aData);
	}
	else if (aNodeName.Compare(KLastFmXspfTrackLocation) == 0)
	{
		//Set track location
		iCurrentTrack->SetLocation(aData);
	}
	else if (aNodeName.Compare(KLastFmXspfTrackTitle) == 0)
	{
		//Set track title
		iCurrentTrack->SetTitle(aData);
	}
	else if (aNodeName.Compare(KLastFmXspfTrackArtist) == 0)
	{
		//Set track artist
		iCurrentTrack->SetArtist(aData);
	}
	else if (aNodeName.Compare(KLastFmXspfTrackAlbum) == 0)
	{
		//Set track album
		iCurrentTrack->SetAlbum(aData);
	}
	else if (aNodeName.Compare(KLastFmXspfTrackImageUrl) == 0)
	{
		//Set track image url
		iCurrentTrack->SetImageUrl(aData);
	}
}

void CAudioscrobblerEngine::ResponseRequestedL(TDesC8& aResponse)
{
	//Notify
	switch (iRequestType)
	{
	case EScrobblerHandshake:
		//Parse handshake response
		ParseHandshakeL(aResponse);

		//Check if there is any error
		if (iSession.Find(_L("FAILED")) != KErrNotFound)
		{
			iObserver.RunError(iErrorDesc);
		}
		else
		{
			//Handshake ok. We make a xspf
			XspfL();
		}
		break;

	case EScrobblerXspf:
		//Parse handshake response
		ParseXmlL(aResponse);

		//Notify playlist
		iObserver.PlaylistRequestedL(iPlaylist);
	}
}

void CAudioscrobblerEngine::ResponseErrorL(TInt aError)
{
	TBuf<50> errorDesc;
	errorDesc.AppendFormat(_L("Error connecting via http: %d"), aError);

	iObserver.RunError(errorDesc);
}
